package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.utils.uuid.UUID;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.*;

/**
 * 设备Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-02-26
 */
@Slf4j
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper,Device> implements IDeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IoTDAClient iotDAClient;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 查询设备
     * 
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询设备列表
     * 
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        return save(device)?1:0;
    }

    /**
     * 修改设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device)
    {
        return updateById(device)?1:0;
    }

    /**
     * 批量删除设备
     * 
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids))?1:0;
    }

    /**
     * 删除设备信息
     * 
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id)
    {
        return removeById(id)?1:0;
    }

    /**
     * 从物联网平台同步产品列表写入redis
     */
    @Override
    public void syncProductList() {
        //1. 从物联网平台获取产品列表
        //1.1 构建请求对象ListProductsRequest对象
        ListProductsRequest listProductsRequest = new ListProductsRequest();
        //1.2 设置请求参数limit每页查询记录数据50条（保证你需要的所有产品都查询过来）
        listProductsRequest.setLimit(50);
        //1.3 执行请求，获取响应结果ListProductsResponse对象
        ListProductsResponse listProductsResponse = null;
        try {
            listProductsResponse = iotDAClient.listProducts(listProductsRequest);
        } catch (Exception e) {
            log.error("物联网平台-同步产品失败，原因：{}",e.getMessage());
            throw new RuntimeException("物联网平台-同步产品失败");
        }
        //1.4 判断响应状态为是否为200,如果不是抛出异常，提示“物联网平台-同步产品失败”
        if (listProductsResponse.getHttpStatusCode() != 200) {
            log.error("物联网平台-同步产品失败");
            throw new RuntimeException("物联网平台-同步产品失败");
        }
        //1.5 获取products集合产品列表数据
        List<ProductSummary> products = listProductsResponse.getProducts();

        //2. 将产品列表写入redis，将products集合产品列表数据json字符串写入redis
        redisTemplate.opsForValue().set(CacheConstants.IOT_ALL_PRODUCT_LIST, JSONUtil.toJsonStr(products));
    }


    /**
     * 查询所有产品列表
     *
     * @return
     */
    @Override
    public List<ProductVo> allProduct() {

        //1.去缓存里面获取产品列表json字符串数据
        String json = redisTemplate.opsForValue().get(CacheConstants.IOT_ALL_PRODUCT_LIST);

        //2.判断缓存中是否存在数据，如果不存在返回一个空集合
        if (StrUtil.isEmpty(json)) {
            return Collections.emptyList();
        }

        //3.如果存在缓存数据将json字符串转换为List<ProductVo>
        List<ProductVo> productVoList = JSONUtil.toList(json, ProductVo.class);

        //4.返回数据
        return productVoList;
    }

    /**
     * 注册设备
     *
     * @param deviceDto
     */
    @Override
    public void registerDevice(DeviceDto deviceDto) {
        //1.检查设备名称是否重复
        //1.1 构建LambdaQueryWrapper对象
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        //1.2 设置设备名字筛选条件
        queryWrapper.eq(Device::getDeviceName,deviceDto.getDeviceName());
        //1.3 执行查询，获取查询匹配的个数，个数大于0，说明已存在，抛出异常，提示设备名称重复
        if (count(queryWrapper) > 0) {
            log.error("注册设备失败：设备名称 【{}】 重复",deviceDto.getDeviceName());
            throw new RuntimeException("注册设备失败：设备名称 【" + deviceDto.getDeviceName() + "】 重复");
        }

        //2.检查设备标识码是否重复
        //2.1 LambdaQueryWrapper对象清除上面的筛选条件（clear方法）
        queryWrapper.clear();
        //2.2 设置设备设备标识码筛选条件
        queryWrapper.eq(Device::getNodeId,deviceDto.getNodeId());
        //2.3 执行查询，获取查询匹配的个数，个数大于0，说明已存在，抛出异常，提示设备标识码重复
        if (count(queryWrapper) > 0) {
            log.error("注册设备失败：设备标识码 【{}】 重复",deviceDto.getNodeId());
            throw new RuntimeException("注册设备失败：设备标识码 【" + deviceDto.getNodeId() + "】 重复");
        }

        //3.检查同一位置是否绑定了相同的产品
        //1.1 LambdaQueryWrapper对象清除上面的筛选条件（clear方法）
        queryWrapper.clear();
        //1.2 设置筛选条件：binding_location，location_type,physical_location_type,product_key查询有数据代表同一位置绑定了相同产品的设备
        queryWrapper.eq(Device::getBindingLocation,deviceDto.getBindingLocation())
                .eq(Device::getLocationType,deviceDto.getLocationType())
                .eq(Device::getPhysicalLocationType,deviceDto.getPhysicalLocationType())
                .eq(Device::getProductKey,deviceDto.getProductKey());
        //1.3 执行查询，获取查询匹配的个数，个数大于0，说明已存在，抛出异常，提示设同一位置绑定了相同产品的设备
        if (count(queryWrapper) > 0){
            log.error("注册设备失败：同一位置绑定了相同产品的设备");
            throw new RuntimeException("注册设备失败：同一位置绑定了相同产品的设备");
        }

        //4.项目华为IOT平台注册设备
        //4.1 请求参数对象AddDeviceRequest对象
        AddDeviceRequest addDeviceRequest = new AddDeviceRequest();
        //4.2 请求体参数对象AddDevice对象
        AddDevice addDevice = new AddDevice();
        //设备标识码 node_id,device_name,product_id
        addDevice.withNodeId(deviceDto.getNodeId())
                .withDeviceName(deviceDto.getDeviceName())
                .withProductId(deviceDto.getProductKey());

        //实例AuthInfo对象，封装secret（自己生成唯一值）
        AuthInfo authInfo = new AuthInfo();
        String secret = UUID.randomUUID().toString().replaceAll("-","");
        authInfo.withSecret(secret);
        //AddDevice对象封装AuthInfo对象(withAuthInfo)
        addDevice.withAuthInfo(authInfo);

        //AddDeviceRequest对象封装AddDevice对象（withBody）
        addDeviceRequest.withBody(addDevice);
        //4.3 执行请求，获取响应结果AddDeviceResponse对象
        AddDeviceResponse addDeviceResponse = null;
        try {
            addDeviceResponse  = iotDAClient.addDevice(addDeviceRequest);
        } catch (Exception e) {
            log.error("物联网平台-注册设备失败："+e.getMessage());
            throw new RuntimeException("注册设备失败："+e.getMessage());
        }
        //4.4 判断响应状态为是否为200,如果不是抛出异常，提示“物联网平台-注册设备失败”
        if (addDeviceResponse.getHttpStatusCode() != 201) {
            log.error("物联网平台-注册设备失败");
            throw new RuntimeException("注册设备失败：物联网平台-注册设备失败");
        }
        //4.5 提示设备注册成功
        log.info("物联网平台-注册设备【{}】成功", deviceDto.getDeviceName());

        //5.本地保存设备数据到数据库中
        //5.1 将前端传递过来的deviceDto对象数据封装赋值给Device对象
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        //5.2 补全数据
        //iot_id 从AddDeviceResponse对象中获取
        device.setIotId(addDeviceResponse.getDeviceId());
        //secret
        device.setSecret(secret);
        //判断是否是随身设备，如果是，物理位置类型为-1
        if(device.getLocationType()==0){
            device.setPhysicalLocationType(-1);
        }

        //5.3 插入数据库
        try {
            save(device);
        } catch (Exception e) {
            //TODO 远程调用华为IOT删除设备(作业)，如果这里删除失败，人工接入，到IOT平台找到对应设备删除。
            log.error("注册设备失败："+e.getMessage());
            throw new RuntimeException("注册设备失败，原因："+e.getMessage());
        }

    }

    /**
     * 查询设备详情数据
     * @param iotId
     * @return
     */
    @Override
    public DeviceDetailVo queryDeviceDetail(String iotId) {
        DeviceDetailVo deviceDetailVo = new DeviceDetailVo();

        //1.对接IOT平台查询设备详情接口查询设备状态、激活时间
        //1.1 封装请求参数ShowDeviceRequest对象 并补全参数device_id
        ShowDeviceRequest request =  new ShowDeviceRequest();
        request.withDeviceId(iotId);
        //1.2 执行请求showDevice获取响应对象ShowDeviceResponse
        ShowDeviceResponse response = null;
        try {
            response = iotDAClient.showDevice(request);
        }catch(Exception e){
            log.error("查询华为IOT设备详情失败："+e.getMessage());
            e.printStackTrace();
        }
        if (response!=null &&  response.getHttpStatusCode() == 200) {
            //1.3 解析响应数据获取设备状态status、激活时间active_time（2015-12-12T12:12:12.122Z）
            deviceDetailVo.setDeviceStatus(response.getStatus());
            String activeTimeStr = response.getActiveTime();
            if(StrUtil.isNotEmpty(activeTimeStr)) {
                LocalDateTime activeTime = LocalDateTimeUtil.parse(activeTimeStr, DatePattern.UTC_MS_PATTERN);
                //日期时区转换
                activeTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                        .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                        .toLocalDateTime();
                deviceDetailVo.setActiveTime(activeTime);
            }
        }

        //2.根据IOT设备id查询数据库设备详情数据Device对象
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getIotId,iotId);
        Device device = getOne(queryWrapper);
        if(device==null){
            log.error("查询数据库设备详情失败：设备不存在");
            return deviceDetailVo;
        }

        //3.将Device对象数据封装给DeviceDetailVo对象
        BeanUtil.copyProperties(device,deviceDetailVo);

        //4.返回数据
        return deviceDetailVo;
    }

    /**
     * 查询设备最新上报影子数据
     *
     * @param iotId
     * @return
     */
    @Override
    public AjaxResult queryServiceProperties(String iotId) {

        //1.定义返回数据对象List<Map<String,Object>>用于封装返回
        List<Map<String,Object>> data = new ArrayList<>();

        //2.远程调用IOT查询设备影子上报数据
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.withDeviceId(iotId);
        ShowDeviceShadowResponse response = null;
        try {
            response = iotDAClient.showDeviceShadow(request);
        }catch(Exception e){
            log.error("查询华为IOT设备影子失败："+e.getMessage());
            e.printStackTrace();
            throw new RuntimeException("查询华为IOT设备影子失败："+e.getMessage());
        }
        if(response.getHttpStatusCode() != 200) {
            log.error("查询华为IOT设备影子失败");
            throw new RuntimeException("查询华为IOT设备影子失败");
        }
        //3.解析设备影子数据，封装到List<Map<String,Object>>中
        DeviceShadowProperties reported = response.getShadow().get(0).getReported();
        if(ObjectUtil.isNotEmpty(reported)){
            LinkedHashMap<String,Object> properties = (LinkedHashMap<String,Object>)reported.getProperties();
            properties.forEach((key,value)->{
                Map<String,Object> map = new HashMap<>();
                map.put("functionId",key);
                map.put("value",value);

                String eventTimeStr = reported.getEventTime();
                if(StrUtil.isNotEmpty(eventTimeStr)) {

                    LocalDateTime eventTime =  LocalDateTimeUtil.parse(eventTimeStr, "yyyyMMdd'T'HHmmss'Z'");
                    //日期时区转换
                    //日期时区转换
                    eventTime = eventTime.atZone(ZoneId.from(ZoneOffset.UTC))
                            .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                            .toLocalDateTime();
                    map.put("eventTime",eventTime);
                }
                //将map添加到List<Map<String,Object>>中
                data.add(map);
            });
        }else{
            return AjaxResult.warn("暂无最新上报数据");
        }

        //4.封装AjaxResult返回
        return AjaxResult.success(data);
    }

    /**
     * 查询产品详情
     *
     * @param productKey
     * @return
     */
    @Override
    public AjaxResult queryProduct(String productKey) {

        //1.请求参数对象实例ShowProductRequest
        ShowProductRequest request =  new ShowProductRequest();

        //2.封装请求参数，product_id
        request.withProductId(productKey);

        //3.执行请求，获取响应对象ShowProductResponse
        ShowProductResponse response = null;
        try {
            response = iotDAClient.showProduct(request);
        } catch (Exception e) {
            log.error("查询华为IOT产品详情失败："+e.getMessage());
            e.printStackTrace();
            throw new RuntimeException("查询华为IOT产品详情失败："+e.getMessage());
        }

        //4.判断响应状态码，200表示成功，非200表示失败
        if(response.getHttpStatusCode() != 200) {
            log.error("查询华为IOT产品详情失败：");
            throw new RuntimeException("查询华为IOT产品详情失败");
        }

        //5.解析获取里面service_capabilities的对象值,封装返回数据AjaxResult
        return AjaxResult.success("success",response.getServiceCapabilities());
    }
}
